package elastic.log;

import com.alibaba.fastjson.JSON;
import elastic.log.httpclient.HttpProxy;
import elastic.log.httpclientasync.AsyncHttpProxy;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.concurrent.FutureCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author chxch
 *
 */
public class SearchLog {

    /**
     * 配置文件
     */
    final public static String PROPERTIES_FILE = "searchserver.properties";
    /**
     * 索引名称前缀
     */
    final public static String INDEX_NAME_PREFIX = "es-szoa-";
    /**
     * 存储日志的esurl地址
     */
    public static String ES_LOG_URL = "";
    
    /**
     * 当前ES_LOG_URL是否有效，如果无效则对ES_LOG_URL不进行任何操作
     */
    private static boolean urlIsAlive = false;
    private static Logger log = LoggerFactory.getLogger(SearchLog.class);
    
    /**
     * 过滤部分方法，如search，aggregate 等操作暂时不写入日志
     */
    private static Map<String, String> filterList = new HashMap<String, String>();
    
    static{
        final InputStream resourceAsStream = SearchLog.class.getClassLoader()
                .getResourceAsStream(PROPERTIES_FILE);
        final Properties properties = new Properties();

        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            log.error("======SearchLog httpclient.properties加载配置文件出错~======", e);
        } finally {
            if (resourceAsStream != null) {
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    log.error("======httpclient加载配置文件，关闭资源出错~======", e);
                }
            }
        }
        
        filterList.put("search", "");
        filterList.put("aggregate", "");

        // 读取配置文件
        ES_LOG_URL = properties.getProperty("eslog_url");
        
        if(!StringUtils.isEmpty(ES_LOG_URL))
        {
            urlIsAlive = checkUrlAlive(ES_LOG_URL);
            initEs();
            
            CheckTask checkTask = new CheckTask();
            checkTask.start();
        }
    }
    
    public static void init(){
        AsyncHttpProxy.getAsyncHttpProxy();
    }
    
    /**
     * 初始化es中的mapping
     */
    public static void initEs()
    {
        if(!urlIsAlive)
            return;
        
        createMapping(getIndexName());
        createMapping(getAfterIndexName());
    }
    
    /**
     * 检测存储日志的url地址是否有效
     * @param postUrl
     * @return
     */
    public static boolean checkUrlAlive(String postUrl){
       
         return HttpProxy.getHttpProxy().httpGetElk(postUrl);
    }
    
    /**
     * 判断索引名称是否存在
     * @param indexName
     * @return
     */
    public static boolean indexExist(String indexName)
    {
        if(!urlIsAlive)
            return false;
        
        return HttpProxy.getHttpProxy().httpGetElk(ES_LOG_URL + indexName);
    }
    
    /**
     * 获取今天索引名称
     * @return
     */
    public static String getIndexName()
    {
       return INDEX_NAME_PREFIX + getDate();
    }

    /**
     * 获取明天索引名称
     * @return
     */
    public static String getAfterIndexName()
    {
       return INDEX_NAME_PREFIX + getAfterDate(1);
    }
    
    /**
     * 创建新的mapping
     * @param newIndexName
     * @return
     */
    public static boolean createMapping(String newIndexName)
    {
        if(!urlIsAlive)
            return false;
        
        if(!indexExist(newIndexName))
        {
            //准备settings
            final Map settings = new HashMap<String, Map>();
            final Map index = new HashMap<String, String>();
            
            index.put("number_of_shards", "12");
            index.put("max_result_window", "10000000");
            index.put("number_of_replicas", "0");
            
            settings.put("index", index);
            
            //准备mapping
            final Map fields = new HashMap<String, Map>();
            
            final Map projectName = new HashMap<String, String>();
            projectName.put("index", "not_analyzed");
            projectName.put("type", "string");
            projectName.put("doc_values", false);
            fields.put("projectName", projectName);
            
            final Map logType = new HashMap<String, String>();
            logType.put("index", "not_analyzed");
            logType.put("type", "string");
            logType.put("doc_values", false);
            fields.put("logType", logType);
            
            final Map funcName = new HashMap<String, String>();
            funcName.put("index", "not_analyzed");
            funcName.put("type", "string");
            funcName.put("doc_values", false);
            fields.put("funcName", funcName);
            
            final Map id = new HashMap<String, String>();
            id.put("index", "not_analyzed");
            id.put("type", "string");
            id.put("doc_values", false);
            fields.put("id", id);
            
            final Map indexName = new HashMap<String, String>();
            indexName.put("index", "not_analyzed");
            indexName.put("type", "string");
            indexName.put("doc_values", false);
            fields.put("indexName", indexName);

            final Map data = new HashMap<String, String>();
            data.put("index", "no");
            data.put("type", "string");
            data.put("doc_values", false);
            fields.put("data", data);

            final Map retStatus = new HashMap<String, String>();
            retStatus.put("index", "not_analyzed");
            retStatus.put("type", "string");
            retStatus.put("doc_values", false);
            fields.put("retStatus", retStatus);

            final Map retryNo = new HashMap<String, String>();
            retryNo.put("type", "long");
            retryNo.put("doc_values", false);
            fields.put("retryNo", retryNo);

            final Map createTime = new HashMap<String, String>();
            createTime.put("format", "yyyy-MM-dd HH:mm:ss.SSS");
            createTime.put("type", "date");
            createTime.put("doc_values", false);  
            fields.put("createTime", createTime);
            
            final Map errMsg = new HashMap<String, String>();
            errMsg.put("index", "no");
            errMsg.put("type", "string");
            errMsg.put("doc_values", false);
            fields.put("errMsg", errMsg);

            final Map sessionId = new HashMap<String, String>();
            retStatus.put("index", "not_analyzed");
            retStatus.put("type", "string");
            retStatus.put("doc_values", false);
            fields.put("sessionId", sessionId);

            final Map properties = new HashMap<String, Map>();
            properties.put("properties", fields);
            final Map type = new HashMap<String, Map>();
            type.put("all", properties);
            
            final Map map = new HashMap<String, Map>();
            map.put("mappings", type);
            map.put("settings", settings);
            
            final String mappings = JSON.toJSONString(map);
            
            return HttpProxy.getHttpProxy().httpPostElk(ES_LOG_URL + newIndexName, mappings, false);
        }
        
        return false;
    }
    
    /**
     * 像es中写入日志
     * @param operation        操作名称，如add、search等
     * @param index            索引名称
     * @param id               id值
     * @param value            具体的内容
     * @param retryNo          重试次数，为0时表示是重试操作
     * @param projectName      工程项目名称。如：szoa、soa
     * @param logType         http提交类型，主要是同步、异步操作，如：HttpProxy、AsyncHttpProxy
     * @param filterFlag       是否进行过滤检测，true：做过滤检测，false：不做过滤检测
     * @param sessionId       会话sessionId
     */
    public static void postLog(String operation, String index, String id, String value, String errMsg, Integer retryNo, String projectName, String logType, boolean filterFlag,String sessionId)
    {
        if(!urlIsAlive)
            return;
        
        if(filterFlag)
        {
            if(filterList.containsKey(operation))
                return;
        }
        
        final Map<String, String> map = new HashMap<String, String>();
        map.put("funcName", operation);
        map.put("indexName", index);
        map.put("id", id);
        map.put("data", value);
        map.put("errMsg", errMsg);
        map.put("createTime", getCurrentTime());
        map.put("retryNo", retryNo==null?null:String.valueOf(retryNo));
        map.put("projectName", projectName);
        map.put("logType", logType);
        map.put("sessionId", sessionId);
        
        final String indexName = getIndexName();
        
        AsyncHttpProxy.getAsyncHttpProxy().httpPostElk(ES_LOG_URL + indexName + "/all", JSON.toJSONString(map), false, new FutureCallback<HttpResponse>() {

            @Override
            public void completed(HttpResponse response) {

            }

            @Override
            public void failed(Exception e) {
                urlIsAlive = checkUrlAlive(ES_LOG_URL);
            }

            @Override
            public void cancelled() {

            }
        });
    }
    
    /**
     * 获取当前时间
     * @return
     */
    public static String getCurrentTime()
    {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        return sdf.format(new Date());
    }
    
    /**
     * 获取当前日期
     * @return
     */
    public static String getDate()
    {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date());
    }
    
    /**
     * 获取当前日期的后几天或前几天的日期
     * @param n 大于0：后n天日期，小于0：前n天日期
     * @return
     */
    public static String getAfterDate(int n)
    {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DAY_OF_MONTH, n);
        return sdf.format((new Date(calendar.getTimeInMillis())));
    }
    
    /**
     * 传入时间与当前时间进行比较
     * @param hour
     * @param minute
     * @param second
     * @return -1：小于当前时间，0：等于当前时间，1：大于当前时间
     */
    public static int compareTime(int hour, int minute, int second)
    {
        final Calendar calendar = Calendar.getInstance();
        final Calendar anothorCalendar = Calendar.getInstance();
        
        calendar.set(0, 0, 0);
        anothorCalendar.set(0, 0, 0, hour, minute, second);
        
        final Date now = calendar.getTime();
        final Date time = anothorCalendar.getTime();
        
        return time.compareTo(now);
    }
    
    
    /**
     * 检测线程
     * 1.检测 ES_LOG_URL是否有效，每隔1分钟检测一次
     * 2.自动创建下一天的mapping,每天22:00:00后开始创建，每60分钟检测一次
     * 
     * @author chxch
     *
     */
    static class CheckTask extends Thread {

        public void run() {

            int index = 0;
            while (true) {
                
                if(!urlIsAlive)
                    urlIsAlive = checkUrlAlive(ES_LOG_URL);

                index++;
                if(index>=60 && compareTime(22,00,00)<=0)
                {
                    createMapping(getAfterIndexName());
                    index = 0;
                }
                
                try {
                    Thread.sleep(1*60*1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}
